/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package com.xujl.hsns.server;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;

import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IoSessionConfig;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.DatagramSessionConfig;
import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.transport.vmpipe.VmPipeAcceptor;
import org.apache.mina.transport.vmpipe.VmPipeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dyuproject.protostuff.parser.ProtoParser.enum_block_return;
import com.xujl.hsns.carriers.Invoke;
import com.xujl.hsns.codec.HSNSCodecFactory;
import com.xujl.hsns.codec.impl.JSONCodecFactory;
import com.xujl.hsns.codec.impl.JavaObjectCodecFactory;
import com.xujl.hsns.codec.impl.XMLCodecFactory;
import com.xujl.hsns.config.CODECER;
import com.xujl.hsns.config.PROTOCOL;


public class HSNSServer {

	
	
	private final static Logger LOGGER = LoggerFactory.getLogger(HSNSServer.class);
	
    private static final long serialVersionUID = 1L;

    private  int port = 80;

    private IoAcceptor acceptor;
    
    private ServicesClip servicesClip= new ServicesClip();
    
    private int maxReadBufferSize=1024*1024*10;
    
    
    private HSNSCodecFactory codecFactory;
    
    private PROTOCOL protocol;
    
    private CODECER codecer;
    
    private HSNSServer(int port,PROTOCOL protocol,CODECER codecer) {
    	this.port=port;
    	this.codecer=codecer;
    	this.protocol=protocol;
    }
    public static HSNSServer newHSNSServer(int port,PROTOCOL protocol,CODECER codecer){
    	return new HSNSServer(port,protocol,codecer);
    }
    public void start() throws IOException{
    	   initCodecer();
    	   initProtocol();
           LOGGER.info("---------------------Service interface class------------------------");
           LOGGER.info(servicesClip.getServices().toString());
           LOGGER.info("------------------------Service Methods------------------------");
           LOGGER.info(servicesClip.getServiceMethods().toString());
           LOGGER.info("codecFactory class:"+codecFactory.getClass().toString());
           System.err.println("HSNSServer listening on port " + port); 
	}
	/**
	 * @throws IOException
	 */
	private void initProtocol() throws IOException {
		if(PROTOCOL.UDP.equals(protocol))
			udp();
		else if(PROTOCOL.TCP.equals(protocol))
			tcp();
		else vmPipe();
	}
	/**
	 * @throws IOException
	 */
    private void udp() throws IOException {
    	
    	
    	acceptor = new NioDatagramAcceptor();
    	acceptor.setHandler(new HSNSServerHandler(servicesClip));
    	DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
    	chain.addLast("logger", new LoggingFilter()); 
    	chain.addLast("codec", new ProtocolCodecFilter( codecFactory));
//          chain.addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
    	DatagramSessionConfig dcfg = ((NioDatagramAcceptor)acceptor).getSessionConfig();
    	dcfg.setReceiveBufferSize(maxReadBufferSize);
    	dcfg.setSendBufferSize(maxReadBufferSize);
    	dcfg.setMaxReadBufferSize(maxReadBufferSize);
    	dcfg.setReuseAddress(true);
    	acceptor.bind(new InetSocketAddress(port));
    	  LOGGER.info("------------------------udp-----------------------");
    }
    private void tcp() throws IOException {
    	acceptor = new NioSocketAcceptor();
    	acceptor.setHandler(new HSNSServerHandler(servicesClip));
    	DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
    	chain.addLast("logger", new LoggingFilter()); 
    	chain.addLast("codec", new ProtocolCodecFilter( codecFactory));
//          chain.addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
    	IoSessionConfig sessionConfig = acceptor.getSessionConfig();
    	sessionConfig.setMaxReadBufferSize(maxReadBufferSize);
    	sessionConfig.setReadBufferSize(maxReadBufferSize);
    	sessionConfig.setUseReadOperation(true);
    	acceptor.bind(new InetSocketAddress(port));
    }
	private void vmPipe() throws IOException {
		acceptor = new VmPipeAcceptor();
           acceptor.setHandler(new HSNSServerHandler(servicesClip));
           DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
           chain.addLast("logger", new LoggingFilter()); 
           chain.addLast("codec", new ProtocolCodecFilter( codecFactory));
//          chain.addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
   		IoSessionConfig sessionConfig = acceptor.getSessionConfig();
		sessionConfig.setMaxReadBufferSize(maxReadBufferSize);
		sessionConfig.setReadBufferSize(maxReadBufferSize);
		sessionConfig.setUseReadOperation(true);
		acceptor.bind(new VmPipeAddress(port));
	}
	/**
	 * 
	 */
	private void initCodecer() {
		if(CODECER.JSON.equals(codecer)){
			System.out.println(codecer);
    		codecFactory= new JSONCodecFactory(Invoke.class); 
    	}else if(CODECER.XML.equals(codecer)){
    		codecFactory= new XMLCodecFactory(Invoke.class); 
    	}else{
    		codecFactory= new JavaObjectCodecFactory(); 
    	}
	}

    /**
	 * 
	 * @param Interfaceclazz 
	 * @param impl
	 */
	public void addService(Class Interfaceclazz,Object impl) throws Exception{
		servicesClip.addService(Interfaceclazz, impl);
	}
	public Object invoke(String genericString,Object[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		
		Object invoke = servicesClip.invoke(genericString, args);
		return invoke;
	}
	
	public List getServices(){
		return servicesClip.getServices();
	}
	public List getServiceMethods(){
		return servicesClip.getServiceMethods();
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public int getMaxReadBufferSize() {
		return maxReadBufferSize;
	}

	public void setMaxReadBufferSize(int maxReadBufferSize) {
		this.maxReadBufferSize = maxReadBufferSize;
	}
	
	public void close(){
		acceptor.dispose();
	}
}
